17. Using Mockito to Write Navigation Tests
L5 P3 A11 Using Mockito To Write Navigation Tests
As you saw in the video, you'll write an integration test that includes the Navigation component. In doing so, you'll create a mock, using Mockito.
Step 1: Add Gradle Dependencies
- Add the gradle dependencies:
app/build.gradle
// Dependencies for Android instrumented unit tests
androidTestImplementation "org.mockito:mockito-core:$mockitoVersion"
androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito:$dexMakerVersion"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
Step 2: Create TasksFragmentTest
- Open
TasksFragment. - Right click on the
TasksFragmentclass name and select Generate then Test. Create a test in theandroidTestsource set. - Copy over this code to the
TasksFragmentTest:
TasksFragmentTest.kt
@RunWith(AndroidJUnit4::class)
@MediumTest
@ExperimentalCoroutinesApi
class TasksFragmentTest {
private lateinit var repository: TasksRepository
@Before
fun initRepository() {
repository = FakeAndroidTestRepository()
ServiceLocator.tasksRepository = repository
}
@After
fun cleanupDb() = runBlockingTest {
ServiceLocator.resetRepository()
}
}
- Add the test
clickTask_navigateToDetailFragmentOne:
TasksFragmentTest.kt
@Test
fun clickTask_navigateToDetailFragmentOne() = runBlockingTest {
repository.saveTask(Task("TITLE1", "DESCRIPTION1", false, "id1"))
repository.saveTask(Task("TITLE2", "DESCRIPTION2", true, "id2"))
// GIVEN - On the home screen
val scenario = launchFragmentInContainer<TasksFragment>(Bundle(), R.style.AppTheme)
}
- Use Mockito's
mockfunction to create a mock:
TasksFragmentTest.kt
val navController = mock(NavController::class.java)
- Make your new mock the fragment's
NavController:
TasksFragmentTest.kt
scenario.onFragment {
Navigation.setViewNavController(it.view!!, navController)
}
- Add the code to click on the item in the
RecyclerViewthat has the text "TITLE1":
TasksFragmentTest.kt
// WHEN - Click on the first list item
onView(withId(R.id.tasks_list))
.perform(RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
hasDescendant(withText("TITLE1")), click()))
RecyclerViewActions is part of the espresso-contrib library and lets you perform Espresso actions on a RecyclerView.
- Verify that navigate was called, with the correct argument:
TasksFragmentTest.kt
// THEN - Verify that we navigate to the first detail screen
verify(navController).navigate(
TasksFragmentDirections.actionTasksFragmentToTaskDetailFragment( "id1")
The complete test looks like this:
TasksFragmentTest.kt
@Test
fun clickTask_navigateToDetailFragmentOne() = runBlockingTest {
repository.saveTask(Task("TITLE1", "DESCRIPTION1", false, "id1"))
repository.saveTask(Task("TITLE2", "DESCRIPTION2", true, "id2"))
// GIVEN - On the home screen
val scenario = launchFragmentInContainer<TasksFragment>(Bundle(), R.style.AppTheme)
val navController = mock(NavController::class.java)
scenario.onFragment {
Navigation.setViewNavController(it.view!!, navController)
}
// WHEN - Click on the first list item
onView(withId(R.id.tasks_list))
.perform(RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
hasDescendant(withText("TITLE1")), click()))
// THEN - Verify that we navigate to the first detail screen
verify(navController).navigate(
TasksFragmentDirections.actionTasksFragmentToTaskDetailFragment( "id1")
)
}
- As always, remember to run your test!
In summary, to test navigation you can:
- Use Mockito to create a
NavControllermock. - Attach that mocked
NavControllerto the fragment. - Verify that navigate was called with the correct action and parameter(s).
Optional Step: Optional, write clickAddTaskButton_navigateToAddEditFragment
To see if you can write a navigation test yourself, try this:
- Write the test
clickAddTaskButton_navigateToAddEditFragmentwhich checks that if you click on the + FAB, you navigate to theAddEditTaskFragment.